﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace CommonEngine
{
    public static class EnumExtend
    {
        /// <summary>
        /// 包含枚举类型并返回新值
        /// </summary>
        public static T Include<T>(this Enum value, T append)
        {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(append, type);
            if (parsed.Signed is long)
            {
                result = Convert.ToInt64(value) | (long)parsed.Signed;
            }
            else if (parsed.Unsigned is ulong)
            {
                result = Convert.ToUInt64(value) | (ulong)parsed.Unsigned;
            }

            //return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <summary>
        /// 删除枚举类型并返回新值
        /// </summary>
        public static T Remove<T>(this Enum value, T remove)
        {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(remove, type);
            if (parsed.Signed is long)
            {
                result = Convert.ToInt64(value) & ~(long)parsed.Signed;
            }
            else if (parsed.Unsigned is ulong)
            {
                result = Convert.ToUInt64(value) & ~(ulong)parsed.Unsigned;
            }

            //return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <summary>
        /// 检查枚举类型是否包含值
        /// </summary>
        public static bool Has<T>(this Enum value, T check)
        {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(check, type);
            if (parsed.Signed is long)
                return (Convert.ToInt64(value) & (long)parsed.Signed) == (long)parsed.Signed;
            else if (parsed.Unsigned is ulong)
                return (Convert.ToUInt64(value) & (ulong)parsed.Unsigned) == (ulong)parsed.Unsigned;
            else
                return false;
        }

        /// <summary>
        /// 检查枚举类型是否缺少值
        /// </summary>
        public static bool Missing<T>(this Enum obj, T value)
        {
            return !Has<T>(obj, value);
        }

        /// <summary>
        /// 取枚举中的最大Int值
        /// </summary>
        public static int EnumMaxInt(this Enum obj)
        {
            if (obj == null)
                return 0;

            int Max = int.MinValue;
            int Min = int.MaxValue;
            foreach (int i in Enum.GetValues(obj.GetType()))
            {
                if (i > Max)
                    Max = i;
                if (i < Min)
                    Min = i;
            }

            return Max;
        }
        /// <summary>
        /// 取枚举中的最小Int值
        /// </summary>
        public static int EnumMinInt(this Enum obj)
        {
            if (obj == null)
                return 0;

            int Max = int.MinValue;
            int Min = int.MaxValue;
            foreach (int i in Enum.GetValues(obj.GetType()))
            {
                if (i > Max)
                    Max = i;
                if (i < Min)
                    Min = i;
            }

            return Min;
        }

        /// <summary>
        /// 获取枚举成员的值(this是扩展方法的标志)
        /// </summary>
        public static int ToInt(this Enum obj)
        {
            return Convert.ToInt32(obj);
        }

        /// <summary>
        /// 取枚举的DefaultValue配置的特性值
        /// </summary>
        public static string DefaultValue(this Enum item)
        {
            DefaultValueAttribute customAttribute = Attribute.GetCustomAttribute(item.GetType().GetField(item.ToString()), typeof(DefaultValueAttribute)) as DefaultValueAttribute;
            return (customAttribute == null ? string.Empty : customAttribute.Value.ToString());
        }
        /// <summary>
        /// 取枚举名称
        /// </summary>
        public static string KeyName(this Enum item)
        {
            return Enum.GetName(item.GetType(), item);
        }
        /// <summary>
        /// 取枚举特性配置，是否必填
        /// </summary>
        public static bool Required(this Enum item)
        {
            return !Attribute.GetCustomAttribute(item.GetType().GetField(item.ToString()), typeof(RequiredAttribute)).IsNull();
        }

        /// <summary>
        /// 字符串转枚举
        /// </summary>
        public static T ToEnum<T>(this string value) where T : struct
        {
            T res;
            if (string.IsNullOrEmpty(value))
                return default(T);

            Enum.TryParse<T>(value, true, out res);
            return res;
        }

        /// <summary>
        /// Int转枚举
        /// </summary>
        public static T ToEnum<T>(this int value) where T : struct
        {
            //(Sex)user.Dic_Sex;    //若user.Dic_Sex=8 ，也是成立的，枚举项失效而已
            //强转会导致得到枚举之外的值,对象依旧成立
            var values = from Enum e in Enum.GetValues(typeof(T))
                         select new { id = e.ToInt(), name = e.ToString() };

            var dic = values.ToDictionary(a => a.id, b => b.name);
            if (dic.ContainsKey(value))
            {
                T res;
                Enum.TryParse<T>(dic[value], true, out res);
                return res;
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 获取指定枚举成员的描述
        /// </summary>
        public static string ToDescriptionString(this Enum obj)
        {
            var attribs = (DescriptionAttribute[])obj.GetType().GetField(obj.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attribs.Length > 0 ? attribs[0].Description : obj.ToString();
        }

        /// <summary>
        /// 根据枚举值，获取指定枚举类的成员描述
        /// </summary>
        public static string GetDescriptionString(this Enum obj, int? id)
        {
            var values = from Enum e in Enum.GetValues(obj.GetType())
                         select new { id = e.ToInt(), name = e.ToDescriptionString() };

            if (!id.HasValue)
                id = 1;

            try
            {
                return values.ToList().Find(c => c.id == id).name;
            }
            catch
            {
                return "";
            }
        }

        /// <summary>
        /// 根据枚举类取描述列表
        /// </summary>
        public static Dictionary<int, string> GetEnumDic(this Enum obj)
        {
            var values = from Enum e in Enum.GetValues(obj.GetType())
                         select new { id = e.ToInt(), name = e.ToDescriptionString() };

            return values.ToDictionary(a => a.id, b => b.name);
        }

        #region Helper Classes

        //class to simplfy narrowing values between
        //a ulong and long since either value should
        //cover any lesser value
        private class _Value
        {
            //cached comparisons for tye to use
            private static Type _UInt64 = typeof(ulong);
            private static Type _UInt32 = typeof(long);

            public long? Signed;
            public ulong? Unsigned;

            public _Value(object value, Type type)
            {

                //make sure it is even an enum to work with
                if (!type.IsEnum)
                {
                    //throw new ArgumentException("Value provided is not an enumerated type!");
                    return; //不发生副作用
                }

                //then check for the enumerated value
                Type compare = Enum.GetUnderlyingType(type);

                //if this is an unsigned long then the only
                //value that can hold it would be a ulong
                if (compare.Equals(_Value._UInt32) || compare.Equals(_Value._UInt64))
                {
                    this.Unsigned = Convert.ToUInt64(value);
                }
                //otherwise, a long should cover anything else
                else
                {
                    this.Signed = Convert.ToInt64(value);
                }
            }
        }

        #endregion

    }
}
